package com.test.controller;

import com.auth0.jwt.interfaces.DecodedJWT;
import com.test.entity.User;
import com.test.exception.ExceptionEnums;
import com.test.exception.MyException;
import com.test.service.UserService;
import com.test.util.jwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/user")
@Slf4j
public class LoginController {

    /**
     * JWT token前缀
     */
    public static final String JWT_TOKEN = "token_jwt_";

    @Autowired
    private UserService userService;

    /**
     * 用户注册
     *
     * @param user 注册用户信息
     * @return
     */
    @RequestMapping("/registry")
    public String userRegistry(User user) {
        try {
            userService.userRegistry(user);
            return "redirect:/login.jsp";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "redirect:/registry.jsp";
    }

    /**
     * 用户退出登录
     *
     * @return
     */
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 退出
     *
     * @return
     * @throws Exception
     */
    @RequestMapping("/logout")
    public String logout(HttpServletRequest request, HttpServletResponse response) throws Exception {
        log.info("-----------------进入了logout方法---------------------");
        String jwtToken = request.getHeader("token");
        if (jwtToken == null) {
            throw new RuntimeException("token无效");
        }
        DecodedJWT decodedJWT = jwtUtil.verifyToken(jwtToken);
        String username = decodedJWT.getClaim("userName").asString();
        log.info("当前用户[{}]", username);
        String key = JWT_TOKEN + username;
        //删除redis中的token
        redisTemplate.delete(key);
        //删除请求头中的token
        response.setHeader("Authorization", null);

        //用户虽然退出登录了,但是token还在有效期内,拿着token仍然可以访问,所以查询当前token的剩余时间
        // 将此token放在redis中,剩余时间作为redis的过期时间,每次访问之前先去redis中获取退出的token判断,可以在JwtFilter判断
        //将key放到redis中之前,需要先设置序列化,不然key会有变化
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        Long expiresTime = decodedJWT.getExpiresAt().getTime();
        log.info("退出用户的token过期时间为 [{}]", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(expiresTime));
        redisTemplate.opsForValue().set("logout_" + jwtToken, username, (expiresTime - System.currentTimeMillis()), TimeUnit.MILLISECONDS);
        log.info("当前用户为: [{}]", SecurityUtils.getSubject());
        SecurityUtils.getSubject().logout();  //执行此方法的时候,会执行redis缓存的清理方法,清除redis中权限的缓存数据
        return "退出成功";
    }

    /**
     * 用户登录 ,shrio整合jwt进行认证
     * User  用户信息  用户名 密码
     *
     * @return
     */
    @PostMapping("/login")
    public Map login(@RequestBody User user, HttpServletResponse response) {
        log.info("+++++++++++++++++++++++用户登录+++++++++++++++++++++++++++++");
        HashMap<String, String> resultMap = new HashMap<>();
        //因为在config中已经配置了SecurityManager,并且也在ShiroFilter中配置了安全管理器,所以在此处可以直接使用SecurityUtiles工具类

        //用户名查询用户
        User user_db = userService.findUserByNmae(user.getUsername());
        if (user_db == null) {
            throw new MyException(ExceptionEnums.INVALID_USERNAME_PASSWORD);
        }

        //如果数据库密码加密了,登录用户密码加密之后进行比较.此处假设正确,然后生成token
        HashMap<String, String> userMap = new HashMap<>();
        userMap.put("userName", user.getUsername());
        String jwtToken = jwtUtil.getToken(userMap);

        //将key放到redis中之前,需要先设置序列化,不然key会有变化
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        //将username token放在redis中 设置过期时间
        String key = JWT_TOKEN + user.getUsername();    //token_jwt_zhangsan
        redisTemplate.opsForValue().set(key, jwtToken, 30, TimeUnit.MINUTES);
        log.info("存入到redis中的用户key为 : [{}] , value 为 : [{}]", key, jwtToken);

        // response携带jwt
        response.setHeader("token", jwtToken);
        response.setHeader("Access-control-Expose-Headers", "token");
        resultMap.put("code", "200");
        resultMap.put("msg", "登录成功");
        resultMap.put("token", jwtToken);
        log.info("信息{}", SecurityUtils.getSubject().getPrincipals());
        return resultMap;
    }

    @RequiresRoles("admin")
    @GetMapping("showAll")
    public Map showAllController(@RequestBody String username) {
        Subject subject = SecurityUtils.getSubject();
        subject.isAuthenticated();
        System.out.println(subject.getPrincipals());
        HashMap<String, Object> map = new HashMap<>();
        map.put("aaa", subject.getPrincipals());
        map.put("bbb", subject.getPreviousPrincipals());
        return map;
    }

    @RequiresPermissions("update")
    @GetMapping("/test")
    public ResponseEntity test() {
        System.out.println("test ~~~");
        return ResponseEntity.ok("test ~~~");
    }
}
